<?php
class Jrouter{
	private static $_instance;
	private $routes=array();

	private function __construct(){}
	private function __clone(){}
	public static function inst(){
	  if( null===self::$_instance ){
	    self::$_instance=new self();
		}
		return self::$_instance;
	}
	protected $errors=array('err'=>array(),'wrn'=>array());
	protected function error($msg){ $this->errors['err'][]=$msg; return false;}
	protected function warning($msg){ $this->errors['wrn'][]=$msg; return false;}
	public function getError(){ return $this->errors['err'][ count($this->errors['err'])-1 ]; }
	public function getErrors(){ return $this->errors['err']; }
	public function getWarning(){ return $this->errors['wrn'][ count($this->errors['wrn'])-1 ]; }
	public function getWarnings(){ return $this->errors['wrn']; }
	
	/******
	 *  регистрация правила роутинга
	 *  $method get|post
	 *  $pattern - ркга урлы
	 *  $controller - строка, описывающая контроллер по CLASS/Method or FILE/CLASS/Method or File/func:<name>
	 */
	public function register($method,$pattern,$controller,$getParams=false){
	  if(empty($method) || empty($pattern) || empty($controller)) return false;
	  
	  if(!$controllerStruct=$this->parseControllerString($controller))
	    return $this->error('parse controller fail');
	  
	  if( !isset($this->routes[$method]) ) $this->routes[$method]=array();
	  $this->routes[$method][$pattern]=$controller;
	  return true;
	}
	
	/***********
	 *  <className>/<methodName>
	 *  <path>.php/<className>/<methodName>
	 *  <path>.php/<functionName>
	 **/
	protected function parseControllerString($controller){
	  $result=array();
	  //С путем к файлу
	  if(strpos($controller, '.php/')!==false){
	    $controller=str_replace('{docRoot}', $_SERVER['DOCUMENT_ROOT'], $controller);
	    $withPath=strpos($controller, '.php/');
	    $result['path']=substr($controller, 0, $withPath );
	    $controller=substr($controller, $withPath+5 );
		}
	
	  $t=explode('/',$controller);
	  switch( count($t)==2 ){
	    case 2:
				$result['class']=$t[0];
				$result['method']=$t[1];
				break;
	    case 1:
				$result['function']=$t[0];
				break;
		}
		unset($t);
	  if(empty($result)) return false;
	  return $result;
	}
	
	/*********************
	 *  Возвращает структуру для вызова контроллера:
	 *  - описание файла, класса и метода или функции
	 *  - параметры вызова
	 *******/
	public function resolve($method=false,$url=false){
	  if(!$method){
	    //echo $_SERVER['REQUEST_METHOD'].':'.$_SERVER['REQUEST_URI'];
	    $method=$_SERVER['REQUEST_METHOD'];
	    $url=$_SERVER['REQUEST_URI'];
		}
	  //Если есть адрес в чистом виде
	  //if( isset($this->routes[$method][$url]) ){$this->routes[$method][$url];}
		
		
		//Адрес по частям
		$t=explode('/',$url);
		do{
		  $turl=implode('/', $t);
		  
			if( isset($this->handles[$method][$turl]) ){
			  unset($t);
			  
		    $prm=$this->get_params($this->handles[$method][$turl],$url);
	    	if($prm===false) return false;
	    	
	    	$qs=$this->get_qs($this->handles[$method][$turl],$query_string);
	    	if($qs===false) return false;
		    
		    return (object) array( 'code'=>$this->handles[$method][$turl]->handle, 'params'=>$prm, 'query_string'=>$qs );
			}
			array_pop($t);
		}while( count($t) );
		
		unset($t,$turl);
		return false;
	}
	
	protected function get_params($handle,$url){
	  if( !$handle->params )return array();
	  $prm=array();
	  $eurl=explode('/', $url);
	  
	  $key = array_keys($handle->params);
		$size = sizeOf($key);
		for ($i=0; $i<$size; $i++){
		  //echo '~'.$handle->params[$key[$i]].'~',$eurl[$key[$i]];
		  if( !preg_match($handle->params[$key[$i]] , $eurl[$key[$i]] ) ) return $this->err('prm_unvalid','parametr '.$i.' unvalid ');
		  $prm[]=$eurl[$key[$i]];
		}
		return $prm;
  }
  protected function get_qs($handle,$qs){
	  if( !$handle->query_string )return array();
	  $prm=array();
	  $eqs=explode('&', $qs);
	  

	  $key = array_keys($eqs);	$size = sizeOf($key);
		for ($i=0; $i<$size; $i++){
		  $t=explode( '=', $eqs[ $key[$i] ] );
		  if(empty($handle->query_string[$t[0]])) continue;
		  if( !preg_match($handle->query_string[$t[0]] , $t[1] ) ) return $this->err('query_string_unvalid','parametr '.$t[0].' is unvalid ');
		  $prm[$t[0]]=$t[1];
		}
		return $prm;
  }
  
}
?>